package com.typhoon.rpc.client;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.typhoon.rpc.common.dto.RpcRequest;
import com.typhoon.rpc.common.dto.RpcResponse;
import com.typhoon.rpc.common.util.StringUtil;
import com.typhoon.rpc.registry.ServiceDiscovery;

public class RpcProxy {
	 private static final Logger LOGGER = LoggerFactory.getLogger(RpcProxy.class);

	    private String serviceAddress;

	    private ServiceDiscovery serviceDiscovery;

	    public RpcProxy(String serviceAddress) {
	        this.serviceAddress = serviceAddress;
	    }

	    public RpcProxy(ServiceDiscovery serviceDiscovery) {
	        this.serviceDiscovery = serviceDiscovery;
	    }

	    @SuppressWarnings("unchecked")
	    public <T> T create(final Class<?> interfaceClass) {
	        return create(interfaceClass, "");
	    }

	    /**
	     * 每一次服务调用,都会生成一个代理去执行
	     * 
	     * @param interfaceClass
	     * @param serviceVersion
	     * @return
	     */
	    @SuppressWarnings("unchecked")
	    public <T> T create(final Class<?> interfaceClass, final String serviceVersion) {
	        // 创建动态代理对象
	        return (T) Proxy.newProxyInstance(
	                interfaceClass.getClassLoader(),
	                new Class<?>[]{interfaceClass},
	                new InvocationHandler() {
	                    @Override
	                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//根据调用接口信息创建动态代理类
	                        // 创建 RPC 请求对象并设置请求属性
	                        RpcRequest request = new RpcRequest();//创建请求封装对象
	                        request.setRequestId(UUID.randomUUID().toString());//生成请求唯一标识
	                        request.setInterfaceName(method.getDeclaringClass().getName());//设置接口名称
	                        request.setMethodName(method.getName());//方法名
	                        request.setParameterTypes(method.getParameterTypes());//方法参数类型列表
	                        request.setParameters(args);//方法参数列表
	                        // 获取 RPC 服务地址
	                        if (serviceDiscovery != null) {//如果服务发现中心不为空,使用服务发现工具去注册中心查找接口对应地址
	                            String serviceName = interfaceClass.getName();
	                            if (StringUtil.isNotEmpty(serviceVersion)) {
	                                serviceName += "-" + serviceVersion;
	                            }
	                            serviceAddress = serviceDiscovery.discover(serviceName);
	                            LOGGER.debug("discover service: {} => {}", serviceName, serviceAddress);
	                        }
	                        if (StringUtil.isEmpty(serviceAddress)) {//如果服务列表为空,抛出无可用服务异常
	                            throw new RuntimeException("server address is empty");
	                        }
	                        // 从 RPC 服务地址中解析主机名与端口号
	                        String[] array = StringUtil.split(serviceAddress, ":");//把地址解析成数组(包含服务主机地址和端口)
	                        String host = array[0];//主机
	                        int port = Integer.parseInt(array[1]);//端口
	                        // 创建 RPC 客户端对象并发送 RPC 请求
	                        RpcClient client = new RpcClient(host, port);//创建netty client连接
	                        long time = System.currentTimeMillis();
	                        RpcResponse response = client.send(request);//使用netty client发送请求并同步等待响应结果
	                        LOGGER.debug("time: {}ms", System.currentTimeMillis() - time);
	                        if (response == null) {//如果没有拿到响应抛出异常
	                            throw new RuntimeException("response is null");
	                        }
	                        // 返回 RPC 响应结果
	                        if (response.hasException()) {//如果返回结果又异常,
	                            throw response.getException();
	                        } else {//如果成功,将结果交给
	                            return response.getResult();
	                        }
	                    }
	                }
	        );
	    }
}
